<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSONL相似度数据可视化工具 - Flask版</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vis-network@9.1.2/dist/vis-network.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/vis-network@9.1.2/dist/styles/vis-network.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f8f9fa;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        .header {
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            color: white;
            padding: 2rem 0;
            margin-bottom: 2rem;
            border-radius: 0 0 20px 20px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        .card {
            border-radius: 15px;
            box-shadow: 0 6px 15px rgba(0,0,0,0.08);
            transition: transform 0.3s;
            margin-bottom: 20px;
            border: none;
        }
        .card:hover {
            transform: translateY(-5px);
        }
        .upload-area {
            border: 2px dashed #6c757d;
            border-radius: 10px;
            padding: 30px;
            text-align: center;
            background-color: #f8f9fa;
            cursor: pointer;
            transition: all 0.3s;
        }
        .upload-area:hover {
            border-color: #0d6efd;
            background-color: #e9f0ff;
        }
        .upload-area.drag-over {
            border-color: #0d6efd;
            background-color: #d1e0ff;
        }
        .chart-container {
            position: relative;
            height: 300px;
            width: 100%;
        }
        .network-container {
            height: 400px;
            border-radius: 10px;
            background-color: white;
            border: 1px solid #e0e0e0;
        }
        .similarity-badge {
            font-size: 0.85rem;
            padding: 0.35em 0.65em;
        }
        .table-hover tbody tr:hover {
            background-color: rgba(13, 110, 253, 0.05);
        }
        .stats-card {
            text-align: center;
            padding: 15px;
        }
        .stats-value {
            font-size: 1.8rem;
            font-weight: bold;
            color: #0d6efd;
        }
        .stats-label {
            font-size: 0.9rem;
            color: #6c757d;
        }
        .section-title {
            border-left: 4px solid #0d6efd;
            padding-left: 12px;
            margin: 25px 0 15px;
            color: #343a40;
        }
        .footer {
            text-align: center;
            padding: 20px;
            margin-top: 30px;
            color: #6c757d;
            font-size: 0.9rem;
        }
        
        /* 对比视图样式 */
        .comparison-container {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        
        /* 优化分页样式 */
        .pagination-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 5px;
            margin-top: 10px;
        }
        .pagination .page-link {
            padding: 0.25rem 0.5rem;
            font-size: 0.875rem;
            min-width: 36px;
            text-align: center;
        }
        .pagination .page-item.disabled .page-link {
            opacity: 0.6;
        }
        .pagination .page-item.active .page-link {
            background-color: #0d6efd;
            border-color: #0d6efd;
        }
        @media (max-width: 768px) {
            .pagination .page-link {
                padding: 0.2rem 0.4rem;
                font-size: 0.8rem;
                min-width: 32px;
            }
            .pagination {
                flex-wrap: wrap;
                justify-content: center;
            }
        }
        /* 紧凑分页样式 */
        .compact-pagination .page-link {
            min-width: 32px;
            text-align: center;
        }
        .compact-pagination .page-item:not(.active) .page-link {
            background-color: #f8f9fa;
            border-color: #dee2e6;
        }
        .doc-panel {
            flex: 1;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        .doc-header {
            background-color: #e9ecef;
            padding: 6px 10px;
            font-weight: bold;
            border-bottom: 1px solid #dee2e6;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-size: 0.9rem;
            min-height: 32px;
        }
        .doc-content-container {
            flex: 1;
            overflow: hidden;
            position: relative;
            min-height: 400px;
            max-height: calc(100vh - 60px);
            display: flex;
            flex-direction: column;
        }
        .doc-content {
            padding: 10px;
            height: 100%;
            overflow-y: auto;
            background-color: white;
            line-height: 1.4;
            white-space: pre-wrap;
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 13px;
            box-sizing: border-box;
        }
        .content-toolbar {
            display: flex;
            justify-content: space-between;
            margin-bottom: 3px;
            align-items: center;
            padding: 3px 0;
            min-height: 32px;
        }
        .line-number {
            position: absolute;
            left: 0;
            top: 0;
            width: 35px;
            text-align: right;
            padding: 10px 3px;
            background-color: #f8f9fa;
            border-right: 1px solid #dee2e6;
            font-family: monospace;
            color: #6c757d;
            z-index: 1;
            font-size: 12px;
        }
        .doc-content-wrapper {
            position: relative;
            padding-left: 35px;
            flex: 1;
            overflow: hidden;
            height: 100%;
        }
        .doc-content {
            padding-left: 8px;
            height: 100%;
            overflow-y: auto;
            box-sizing: border-box;
        }
        .tool-btn:hover {
            background-color: #dde0e3;
        }
        .tool-btn.active {
            background-color: #0d6efd;
            color: white;
            border-color: #0d6efd;
        }
        .sync-indicator {
            display: flex;
            align-items: center;
            gap: 5px;
            font-size: 0.9rem;
        }
        .sync-toggle {
            position: relative;
            display: inline-block;
            width: 40px;
            height: 20px;
        }
        .sync-toggle input {
            opacity: 0;
            width: 0;
            height: 0;
        }
        .sync-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 20px;
        }
        .sync-slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 2px;
            bottom: 2px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
        input:checked + .sync-slider {
            background-color: #0d6efd;
        }
        input:checked + .sync-slider:before {
            transform: translateX(20px);
        }
        .line-number {
            position: absolute;
            left: 0;
            top: 0;
            width: 35px;
            text-align: right;
            padding: 10px 3px;
            background-color: #f8f9fa;
            border-right: 1px solid #dee2e6;
            font-family: monospace;
            color: #6c757d;
            z-index: 1;
            font-size: 12px;
        }
        .doc-content-wrapper {
            position: relative;
            padding-left: 35px;
            flex: 1;
            overflow: hidden;
            height: 100%;
        }
        .doc-content {
            padding-left: 8px;
            height: 100%;
            overflow-y: auto;
            box-sizing: border-box;
        }
        .loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
        }
        .loading-spinner {
            color: white;
            font-size: 1.5rem;
        }
        
        /* 高亮样式 */
        .highlight {
            background-color: #fff3cd;
            padding: 2px 0;
        }
        
        .diff-highlight {
            background-color: #f8d7da;
        }
        
        .similar-highlight {
            background-color: #d4edda;
        }
        
        .content-line {
            padding: 2px 5px;
            margin: 1px 0;
            border-radius: 2px;
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="container">
            <h1 class="text-center">JSONL相似度数据可视化工具 - Flask版</h1>
            <p class="text-center lead">上传JSONL文件，可视化文档相似度关系</p>
        </div>
    </div>

    <div class="container">
        <!-- 文件上传区域 -->
        <div class="card">
            <div class="card-body">
                <h5 class="card-title">上传JSONL文件</h5>
                <div id="upload-area" class="upload-area">
                    <div class="mb-3">
                        <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="currentColor" class="bi bi-cloud-arrow-up text-primary" viewBox="0 0 16 16">
                            <path fill-rule="evenodd" d="M7.646 5.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 6.707V10.5a.5.5 0 0 1-1 0V6.707L6.354 7.854a.5.5 0 1 1-.708-.708l2-2z"/>
                            <path d="M4.406 3.342A5.53 5.53 0 0 1 8 2c2.69 0 4.923 2 5.166 4.579C14.758 6.804 16 8.137 16 9.773 16 11.569 14.502 13 12.687 13H3.781C1.708 13 0 11.366 0 9.318c0-1.763 1.266-3.223 2.942-3.593.143-.863.698-1.723 1.464-2.383zm.653.757c-.757.653-1.153 1.44-1.153 2.056v.448l-.445.049C2.064 6.805 1 7.952 1 9.318 1 10.785 2.23 12 3.781 12h8.906C13.98 12 15 10.988 15 9.773c0-1.216-1.02-2.228-2.313-2.228h-.5v-.5C12.188 4.825 10.328 3 8 3a4.53 4.53 0 0 0-2.941 1.1z"/>
                        </svg>
                        <h5>拖放文件到此处或点击上传</h5>
                        <p class="text-muted">支持JSONL格式文件，每行包含doc1_id, doc2_id, similarity, doc1_content, doc2_content</p>
                    </div>
                    <input type="file" id="file-input" class="d-none" accept=".jsonl">
                    <button class="btn btn-primary btn-lg" id="upload-btn">选择文件</button>
                </div>
                <div id="file-info" class="mt-3 d-none">
                    <p>已选择文件: <span id="file-name"></span></p>
                    <button class="btn btn-success" id="process-btn">处理文件</button>
                </div>
                <div id="upload-progress" class="mt-3 d-none">
                    <div class="progress">
                        <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
                    </div>
                    <p class="text-center mt-2">正在处理文件...</p>
                </div>
            </div>
        </div>

        <!-- 数据统计卡片 -->
        <div id="stats-section" class="d-none">
            <h3 class="section-title">数据概览</h3>
            <div class="row">
                <div class="col-md-3">
                    <div class="card stats-card">
                        <div class="stats-value" id="pair-count">0</div>
                        <div class="stats-label">文档对数量</div>
                    </div>
                </div>
                <div class="col-md-3">
                    <div class="card stats-card">
                        <div class="stats-value" id="doc-count">0</div>
                        <div class="stats-label">唯一文档数量</div>
                    </div>
                </div>
                <div class="col-md-3">
                    <div class="card stats-card">
                        <div class="stats-value" id="avg-similarity">0.00</div>
                        <div class="stats-label">平均相似度</div>
                    </div>
                </div>
                <div class="col-md-3">
                    <div class="card stats-card">
                        <div class="stats-value" id="max-similarity">0.00</div>
                        <div class="stats-label">最高相似度</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 数据表格 -->
        <div id="data-section" class="d-none">
            <h3 class="section-title">文档相似度数据</h3>
            <div class="card">
                <div class="card-body">
                    <!-- 语言过滤、排序和分页控制 -->
                    <div class="row mb-3">
                        <div class="col-md-4">
                            <div class="d-flex align-items-center">
                                <label class="me-2 mb-0">语言过滤:</label>
                                <select class="form-select form-select-sm" id="language-filter" style="width: auto;">
                                    <option value="all">全部数据</option>
                                    <option value="chinese">中文数据对</option>
                                    <option value="english">英文数据对</option>
                                </select>
                            </div>
                        </div>
                        <div class="col-md-4">
                            <div class="d-flex align-items-center">
                                <label class="me-2 mb-0">排序:</label>
                                <select class="form-select form-select-sm me-2" id="sort-field" style="width: auto;">
                                    <option value="index">默认顺序</option>
                                    <option value="similarity">相似度</option>
                                    <option value="doc1_id">文档1 ID</option>
                                    <option value="doc2_id">文档2 ID</option>
                                </select>
                                <select class="form-select form-select-sm" id="sort-order" style="width: auto;">
                                    <option value="asc">升序</option>
                                    <option value="desc">降序</option>
                                </select>
                            </div>
                        </div>
                        <div class="col-md-4 text-end">
                            <div class="d-flex align-items-center justify-content-end">
                                <label class="me-2 mb-0">每页显示:</label>
                                <select class="form-select form-select-sm" id="items-per-page" style="width: auto;">
                                    <option value="10">10</option>
                                    <option value="20">20</option>
                                    <option value="50">50</option>
                                    <option value="100">100</option>
                                </select>
                                <button class="btn btn-sm btn-outline-info ms-2" id="export-btn">导出数据</button>
                            </div>
                        </div>
                    </div>
                    
                    <div class="table-responsive">
                        <table class="table table-hover">
                            <thead>
                                <tr>
                                    <th>文档对ID</th>
                                    <th>文档1 ID</th>
                                    <th>文档2 ID</th>
                                    <th>相似度</th>
                                    <th>操作</th>
                                </tr>
                            </thead>
                            <tbody id="data-table-body">
                                <!-- 数据将通过JS填充 -->
                            </tbody>
                        </table>
                    </div>
                    
                    <!-- 分页信息 -->
                    <div class="d-flex justify-content-between align-items-center mb-3">
                        <div class="text-muted">
                            显示第 <span id="page-start">0</span> 到 <span id="page-end">0</span> 条，共 <span id="total-items">0</span> 条记录
                        </div>
                        <div>
                            <button class="btn btn-sm btn-outline-secondary" id="first-page">首页</button>
                            <button class="btn btn-sm btn-outline-secondary" id="prev-page">上一页</button>
                            <span class="mx-2">
                                第 <input type="number" id="page-input" min="1" max="1" value="1" style="width: 60px; text-align: center;"> 页
                            </span>
                            <button class="btn btn-sm btn-outline-secondary" id="next-page">下一页</button>
                            <button class="btn btn-sm btn-outline-secondary" id="last-page">末页</button>
                        </div>
                    </div>
                    
                    <!-- 分页导航 -->
                    <nav>
                        <ul class="pagination justify-content-center" id="pagination">
                            <!-- 分页将通过JS生成 -->
                        </ul>
                    </nav>
                </div>
            </div>
        </div>

        <!-- 可视化区域 -->
        <div id="visualization-section" class="d-none">
            <h3 class="section-title">数据可视化</h3>
            
            <div class="row mb-4">
                <div class="col-md-6">
                    <div class="card">
                        <div class="card-body">
                            <h5 class="card-title">相似度分布</h5>
                            <div class="chart-container">
                                <canvas id="similarity-chart"></canvas>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="card">
                        <div class="card-body">
                            <h5 class="card-title">相似度散点图</h5>
                            <div class="chart-container">
                                <canvas id="scatter-chart"></canvas>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="card mb-4">
                <div class="card-body">
                    <h5 class="card-title">文档关系网络</h5>
                    <div class="network-container" id="network-graph"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- 文档内容模态框 - 对比视图 -->
    <div class="modal fade content-modal" id="content-modal" tabindex="-1">
        <div class="modal-dialog modal-fullscreen">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="content-modal-title">文档内容对比</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <div class="content-toolbar">
                        <div>
                            <span class="badge bg-info">相似度: <span id="modal-similarity">0.00</span></span>
                        </div>
                        <div style="display: flex; gap: 10px;">
                            <button class="tool-btn active" id="highlight-btn">高亮相似内容</button>
                            <button class="tool-btn" id="diff-btn">高亮差异内容</button>
                            <div class="sync-indicator">
                                <span>同步滚动:</span>
                                <label class="sync-toggle">
                                    <input type="checkbox" id="sync-toggle" checked>
                                    <span class="sync-slider"></span>
                                </label>
                            </div>
                        </div>
                    </div>
                    
                    <div class="comparison-container">
                        <div class="doc-panel">
                            <div class="doc-header doc1-header">
                                <span id="doc1-title">文档1</span>
                                <span class="badge bg-primary">文档1</span>
                            </div>
                            <div class="doc-content-container">
                                <div class="doc-content-wrapper">
                                    <div class="line-number" id="doc1-line-numbers"></div>
                                    <div class="doc-content" id="doc1-content"></div>
                                </div>
                            </div>
                        </div>
                        
                        <div class="doc-panel">
                            <div class="doc-header doc2-header">
                                <span id="doc2-title">文档2</span>
                                <span class="badge bg-success">文档2</span>
                            </div>
                            <div class="doc-content-container">
                                <div class="doc-content-wrapper">
                                    <div class="line-number" id="doc2-line-numbers"></div>
                                    <div class="doc-content" id="doc2-content"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
                </div>
            </div>
        </div>
    </div>

    <!-- 加载遮罩 -->
    <div id="loading-overlay" class="loading-overlay d-none">
        <div class="loading-spinner">
            <div class="spinner-border" role="status">
                <span class="visually-hidden">加载中...</span>
            </div>
            <div class="mt-2">正在处理，请稍候...</div>
        </div>
    </div>

    <div class="footer">
        <p>JSONL相似度数据可视化工具 - Flask版 &copy; 2023</p>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        // 全局变量
        let currentDataId = null;
        let currentPage = 1;
        let itemsPerPage = 10;
        let currentSortField = 'index';
        let currentSortOrder = 'asc';
        let currentLanguageFilter = 'all';
        let currentHighlightMode = 'similar';
        let syncScrolling = true;
        let isScrolling = false;
        let currentTableData = []; // 存储当前表格数据

        // DOM元素
        const uploadArea = document.getElementById('upload-area');
        const fileInput = document.getElementById('file-input');
        const uploadBtn = document.getElementById('upload-btn');
        const fileInfo = document.getElementById('file-info');
        const fileName = document.getElementById('file-name');
        const processBtn = document.getElementById('process-btn');
        const uploadProgress = document.getElementById('upload-progress');
        const statsSection = document.getElementById('stats-section');
        const dataSection = document.getElementById('data-section');
        const visualizationSection = document.getElementById('visualization-section');
        const tableBody = document.getElementById('data-table-body');
        const pagination = document.getElementById('pagination');
        const pairCount = document.getElementById('pair-count');
        const docCount = document.getElementById('doc-count');
        const avgSimilarity = document.getElementById('avg-similarity');
        const maxSimilarity = document.getElementById('max-similarity');
        const highlightBtn = document.getElementById('highlight-btn');
        const diffBtn = document.getElementById('diff-btn');
        const syncToggle = document.getElementById('sync-toggle');
        const loadingOverlay = document.getElementById('loading-overlay');
        
        // 控制元素
        const languageFilter = document.getElementById('language-filter');
        const sortField = document.getElementById('sort-field');
        const sortOrder = document.getElementById('sort-order');
        const itemsPerPageSelect = document.getElementById('items-per-page');
        const pageStart = document.getElementById('page-start');
        const pageEnd = document.getElementById('page-end');
        const totalItems = document.getElementById('total-items');
        const firstPageBtn = document.getElementById('first-page');
        const prevPageBtn = document.getElementById('prev-page');
        const pageInput = document.getElementById('page-input');
        const nextPageBtn = document.getElementById('next-page');
        const lastPageBtn = document.getElementById('last-page');
        const exportBtn = document.getElementById('export-btn');
        
        // 初始化事件监听器
        uploadBtn.addEventListener('click', () => fileInput.click());
        fileInput.addEventListener('change', handleFileSelect);
        processBtn.addEventListener('click', processFile);
        highlightBtn.addEventListener('click', () => setHighlightMode('similar'));
        diffBtn.addEventListener('click', () => setHighlightMode('diff'));
        syncToggle.addEventListener('change', function() {
            syncScrolling = this.checked;
        });
        
        // 拖放功能
        uploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            uploadArea.classList.add('drag-over');
        });
        
        uploadArea.addEventListener('dragleave', () => {
            uploadArea.classList.remove('drag-over');
        });
        
        uploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            uploadArea.classList.remove('drag-over');
            
            if (e.dataTransfer.files.length) {
                fileInput.files = e.dataTransfer.files;
                handleFileSelect();
            }
        });

        // 控制事件监听器
        sortField.addEventListener('change', handleSortChange);
        sortOrder.addEventListener('change', handleSortChange);
        itemsPerPageSelect.addEventListener('change', handleItemsPerPageChange);
        languageFilter.addEventListener('change', handleLanguageFilterChange);
        firstPageBtn.addEventListener('click', goToFirstPage);
        prevPageBtn.addEventListener('click', goToPrevPage);
        nextPageBtn.addEventListener('click', goToNextPage);
        lastPageBtn.addEventListener('click', goToLastPage);
        pageInput.addEventListener('change', handlePageInputChange);
        exportBtn.addEventListener('click', exportData);

        // 处理文件选择
        function handleFileSelect() {
            if (fileInput.files.length === 0) return;
            
            const file = fileInput.files[0];
            fileName.textContent = file.name;
            fileInfo.classList.remove('d-none');
        }

        // 处理文件上传
        async function processFile() {
            const file = fileInput.files[0];
            if (!file) return;
            
            const formData = new FormData();
            formData.append('file', file);
            
            showLoading();
            
            try {
                const response = await fetch('/upload', {
                    method: 'POST',
                    body: formData
                });
                
                // 检查响应状态
                if (!response.ok) {
                    // 如果响应不是JSON，可能是服务器错误页面
                    const contentType = response.headers.get('content-type');
                    if (contentType && contentType.includes('application/json')) {
                        const errorResult = await response.json();
                        throw new Error(errorResult.error || '上传失败');
                    } else {
                        throw new Error(`服务器错误: ${response.status} ${response.statusText}`);
                    }
                }
                
                const result = await response.json();
                
                if (result.success) {
                    currentDataId = result.data_id;
                    
                    // 更新统计信息
                    updateStats(result.stats);
                    
                    // 显示数据部分
                    statsSection.classList.remove('d-none');
                    dataSection.classList.remove('d-none');
                    visualizationSection.classList.remove('d-none');
                    
                    // 加载数据表格和可视化
                    await loadTableData();
                    await loadVisualizationData();
                    
                    // 隐藏文件信息
                    fileInfo.classList.add('d-none');
                    
                } else {
                    alert('文件处理失败: ' + result.error);
                }
            } catch (error) {
                alert('上传失败: ' + error.message);
                console.error('上传错误详情:', error);
            } finally {
                hideLoading();
            }
        }

        // 更新统计信息
        function updateStats(stats) {
            pairCount.textContent = stats.pair_count;
            docCount.textContent = stats.doc_count;
            avgSimilarity.textContent = stats.avg_similarity.toFixed(2);
            maxSimilarity.textContent = stats.max_similarity.toFixed(2);
        }

        // 加载表格数据
        async function loadTableData() {
            if (!currentDataId) return;
            
            const url = `/api/data/${currentDataId}?page=${currentPage}&per_page=${itemsPerPage}&sort_field=${currentSortField}&sort_order=${currentSortOrder}&language_filter=${currentLanguageFilter}`;
            
            try {
                const response = await fetch(url);
                const result = await response.json();
                
                if (response.ok) {
                    renderTable(result.data, result.pagination);
                } else {
                    alert('加载数据失败: ' + result.error);
                }
            } catch (error) {
                alert('网络错误: ' + error.message);
            }
        }

        // 渲染表格
        function renderTable(data, paginationInfo) {
            // 清空表格
            tableBody.innerHTML = '';
            
            // 存储当前表格数据用于索引映射
            currentTableData = data;
            
            // 填充表格
            data.forEach((item, index) => {
                const row = document.createElement('tr');
                
                // 根据相似度设置颜色
                let badgeClass = 'bg-secondary';
                if (item.similarity > 0.8) badgeClass = 'bg-success';
                else if (item.similarity > 0.6) badgeClass = 'bg-primary';
                else if (item.similarity > 0.4) badgeClass = 'bg-info';
                else if (item.similarity > 0.2) badgeClass = 'bg-warning';
                
                row.innerHTML = `
                    <td>${paginationInfo.start_index + index}</td>
                    <td>${item.doc1_id}</td>
                    <td>${item.doc2_id}</td>
                    <td>
                        <span class="badge ${badgeClass} similarity-badge">${item.similarity.toFixed(2)}</span>
                    </td>
                    <td>
                        <button class="btn btn-sm btn-outline-primary view-content" 
                                data-index="${item.index}">
                            对比内容
                        </button>
                    </td>
                `;
                
                tableBody.appendChild(row);
            });
            
            // 添加事件监听器
            document.querySelectorAll('.view-content').forEach((button, index) => {
                button.addEventListener('click', function() {
                    // 使用表格行中的索引，而不是数据项中的索引
                    showContentModal(index);
                });
            });
            
            // 更新分页信息
            updatePaginationInfo(paginationInfo);
            
            // 渲染分页
            renderPagination(paginationInfo);
        }

        // 更新分页信息
        function updatePaginationInfo(paginationInfo) {
            pageStart.textContent = paginationInfo.start_index;
            pageEnd.textContent = paginationInfo.end_index;
            totalItems.textContent = paginationInfo.total_items;
            
            // 更新页码输入框
            pageInput.value = paginationInfo.current_page;
            pageInput.max = paginationInfo.total_pages;
        }

        // 渲染分页
        function renderPagination(paginationInfo) {
            const totalPages = paginationInfo.total_pages;
            pagination.innerHTML = '';
            
            if (totalPages <= 1) return;
            
            // 添加上一页按钮
            const prevLi = document.createElement('li');
            prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
            prevLi.innerHTML = `<a class="page-link" href="#" title="上一页">«</a>`;
            prevLi.addEventListener('click', (e) => {
                e.preventDefault();
                if (currentPage > 1) {
                    currentPage--;
                    loadTableData();
                }
            });
            pagination.appendChild(prevLi);
            
            // 智能显示页码 - 最多显示5个页码
            const maxVisiblePages = 5;
            let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
            let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
            
            // 调整起始页码
            if (endPage - startPage + 1 < maxVisiblePages) {
                startPage = Math.max(1, endPage - maxVisiblePages + 1);
            }
            
            // 显示第一页和省略号（如果需要）
            if (startPage > 1) {
                const firstLi = document.createElement('li');
                firstLi.className = 'page-item';
                firstLi.innerHTML = `<a class="page-link" href="#" title="第1页">1</a>`;
                firstLi.addEventListener('click', (e) => {
                    e.preventDefault();
                    currentPage = 1;
                    loadTableData();
                });
                pagination.appendChild(firstLi);
                
                if (startPage > 2) {
                    const ellipsisLi = document.createElement('li');
                    ellipsisLi.className = 'page-item disabled';
                    ellipsisLi.innerHTML = `<span class="page-link">...</span>`;
                    pagination.appendChild(ellipsisLi);
                }
            }
            
            // 显示当前页码范围
            for (let i = startPage; i <= endPage; i++) {
                const li = document.createElement('li');
                li.className = `page-item ${i === currentPage ? 'active' : ''}`;
                li.innerHTML = `<a class="page-link" href="#" title="第${i}页">${i}</a>`;
                li.addEventListener('click', (e) => {
                    e.preventDefault();
                    currentPage = i;
                    loadTableData();
                });
                pagination.appendChild(li);
            }
            
            // 显示最后一页和省略号（如果需要）
            if (endPage < totalPages) {
                if (endPage < totalPages - 1) {
                    const ellipsisLi = document.createElement('li');
                    ellipsisLi.className = 'page-item disabled';
                    ellipsisLi.innerHTML = `<span class="page-link">...</span>`;
                    pagination.appendChild(ellipsisLi);
                }
                
                const lastLi = document.createElement('li');
                lastLi.className = 'page-item';
                lastLi.innerHTML = `<a class="page-link" href="#" title="第${totalPages}页">${totalPages}</a>`;
                lastLi.addEventListener('click', (e) => {
                    e.preventDefault();
                    currentPage = totalPages;
                    loadTableData();
                });
                pagination.appendChild(lastLi);
            }
            
            // 添加下一页按钮
            const nextLi = document.createElement('li');
            nextLi.className = `page-item ${currentPage === totalPages ? 'disabled' : ''}`;
            nextLi.innerHTML = `<a class="page-link" href="#" title="下一页">»</a>`;
            nextLi.addEventListener('click', (e) => {
                e.preventDefault();
                if (currentPage < totalPages) {
                    currentPage++;
                    loadTableData();
                }
            });
            pagination.appendChild(nextLi);
            
            pagination.classList.add('compact-pagination');
        }

        // 加载可视化数据
        async function loadVisualizationData() {
            if (!currentDataId) return;
            
            try {
                const response = await fetch(`/api/visualization/${currentDataId}`);
                const result = await response.json();
                
                if (response.ok) {
                    renderVisualizations(result);
                } else {
                    alert('加载可视化数据失败: ' + result.error);
                }
            } catch (error) {
                alert('网络错误: ' + error.message);
            }
        }

        // 渲染可视化图表
        function renderVisualizations(data) {
            renderSimilarityChart(data.similarity_distribution);
            renderScatterChart(data.scatter_data);
            renderNetworkGraph(data.network_data);
        }

        // 渲染相似度分布直方图
        function renderSimilarityChart(distributionData) {
            const ctx = document.getElementById('similarity-chart').getContext('2d');
            
            // 准备数据 - 确保bin值是数字
            const labels = distributionData.bins.map((bin, index) => {
                const start = parseFloat(bin) || 0;
                const nextBin = distributionData.bins[index + 1];
                const end = nextBin ? parseFloat(nextBin) : 1.0;
                return `${start.toFixed(1)}-${end.toFixed(1)}`;
            });
            
            // 创建图表
            new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: labels,
                    datasets: [{
                        label: '文档对数量',
                        data: distributionData.counts,
                        backgroundColor: 'rgba(54, 162, 235, 0.6)',
                        borderColor: 'rgba(54, 162, 235, 1)',
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            display: true,
                            position: 'top'
                        },
                        tooltip: {
                            callbacks: {
                                label: function(context) {
                                    return `文档对数量: ${context.parsed.y}`;
                                }
                            }
                        }
                    },
                    scales: {
                        x: {
                            title: {
                                display: true,
                                text: '相似度区间'
                            }
                        },
                        y: {
                            title: {
                                display: true,
                                text: '文档对数量'
                            },
                            beginAtZero: true
                        }
                    }
                }
            });
        }

        // 渲染散点图
        function renderScatterChart(scatterData) {
            const ctx = document.getElementById('scatter-chart').getContext('2d');
            
            // 创建散点图数据
            const scatterDataset = {
                label: '文档相似度分布',
                data: scatterData.map(item => ({
                    x: item.x,
                    y: item.y
                })),
                backgroundColor: 'rgba(255, 99, 132, 0.6)',
                borderColor: 'rgba(255, 99, 132, 1)',
                borderWidth: 1,
                pointRadius: 3
            };
            
            // 创建图表
            new Chart(ctx, {
                type: 'scatter',
                data: {
                    datasets: [scatterDataset]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            display: true,
                            position: 'top'
                        },
                        tooltip: {
                            callbacks: {
                                label: function(context) {
                                    const item = scatterData[context.dataIndex];
                                    return `索引: ${item.index}, 相似度: ${item.y.toFixed(2)}`;
                                }
                            }
                        }
                    },
                    scales: {
                        x: {
                            title: {
                                display: true,
                                text: '文档对索引'
                            },
                            min: 0
                        },
                        y: {
                            title: {
                                display: true,
                                text: '相似度'
                            },
                            min: 0,
                            max: 1
                        }
                    }
                }
            });
        }

        // 渲染网络图
        function renderNetworkGraph(networkData) {
            const container = document.getElementById('network-graph');
            
            // 创建节点和边
            const nodes = new vis.DataSet(networkData.nodes);
            const edges = new vis.DataSet(networkData.edges);
            
            // 网络图配置
            const options = {
                nodes: {
                    shape: 'dot',
                    size: 16,
                    font: {
                        size: 12,
                        face: 'Tahoma'
                    },
                    borderWidth: 2
                },
                edges: {
                    width: 2,
                    color: {
                        color: '#848484',
                        highlight: '#848484',
                        hover: '#848484'
                    },
                    smooth: {
                        type: 'continuous'
                    }
                },
                physics: {
                    stabilization: { iterations: 100 }
                },
                interaction: {
                    tooltipDelay: 200,
                    hideEdgesOnDrag: true
                }
            };
            
            // 创建网络图
            const network = new vis.Network(container, { nodes, edges }, options);
            
            // 添加点击事件
            network.on('click', function(params) {
                if (params.nodes.length > 0) {
                    const nodeId = params.nodes[0];
                    const node = nodes.get(nodeId);
                    alert(`文档ID: ${node.id}\n连接数: ${node.value}`);
                }
            });
        }

        // 设置高亮模式
        function setHighlightMode(mode) {
            currentHighlightMode = mode;
            
            // 更新按钮状态
            highlightBtn.classList.toggle('active', mode === 'similar');
            diffBtn.classList.toggle('active', mode === 'diff');
            
            // 重新高亮当前文档内容，但不重新获取数据
            const currentIndex = document.getElementById('content-modal').getAttribute('data-current-index');
            if (currentIndex !== null) {
                const dataItem = currentTableData[parseInt(currentIndex)];
                if (dataItem) {
                    highlightContent(dataItem.doc1_content, dataItem.doc2_content);
                }
            }
        }

        // 显示文档内容模态框
        async function showContentModal(index) {
            if (!currentDataId) return;
            
            showLoading();
            
            try {
                // 使用当前表格数据中的实际数据项
                const tableIndex = index; // 这是表格行中的索引
                const dataItem = currentTableData[tableIndex]; // 从当前表格数据中获取对应的数据项
                
                if (!dataItem) {
                    throw new Error('无法找到对应的数据项');
                }
                
                // 直接使用当前表格数据中的文档内容，而不是重新从服务器获取
                const modal = new bootstrap.Modal(document.getElementById('content-modal'));
                const modalElement = document.getElementById('content-modal');
                
                // 存储当前索引
                modalElement.setAttribute('data-current-index', tableIndex);
                
                // 更新模态框标题和相似度
                document.getElementById('content-modal-title').textContent = 
                    `文档内容对比 - 文档对 ${tableIndex + 1}`;
                document.getElementById('modal-similarity').textContent = 
                    dataItem.similarity.toFixed(2);
                
                // 更新文档标题
                document.getElementById('doc1-title').textContent = `文档1 (ID: ${dataItem.doc1_id})`;
                document.getElementById('doc2-title').textContent = `文档2 (ID: ${dataItem.doc2_id})`;
                
                // 渲染文档内容 - 直接使用当前表格数据中的内容
                renderDocumentContentFromData(dataItem);
                
                // 显示模态框
                modal.show();
                
            } catch (error) {
                alert('网络错误: ' + error.message);
            } finally {
                hideLoading();
            }
        }

        // 从数据项渲染文档内容
        function renderDocumentContentFromData(dataItem) {
            const doc1Content = document.getElementById('doc1-content');
            const doc2Content = document.getElementById('doc2-content');
            const doc1LineNumbers = document.getElementById('doc1-line-numbers');
            const doc2LineNumbers = document.getElementById('doc2-line-numbers');
            
            // 清空内容
            doc1Content.innerHTML = '';
            doc2Content.innerHTML = '';
            doc1LineNumbers.innerHTML = '';
            doc2LineNumbers.innerHTML = '';
            
            // 使用高亮功能显示文档内容
            highlightContent(dataItem.doc1_content, dataItem.doc2_content);
            
            // 添加同步滚动
            setupSyncScrolling();
        }

        // 渲染文档内容
        async function renderDocumentContent(index, documentData = null) {
            if (!documentData) {
                // 如果没有提供数据，重新获取
                const response = await fetch(`/api/document/${currentDataId}/${index}`);
                documentData = await response.json();
            }
            
            const doc1Content = document.getElementById('doc1-content');
            const doc2Content = document.getElementById('doc2-content');
            const doc1LineNumbers = document.getElementById('doc1-line-numbers');
            const doc2LineNumbers = document.getElementById('doc2-line-numbers');
            
            // 清空内容
            doc1Content.innerHTML = '';
            doc2Content.innerHTML = '';
            doc1LineNumbers.innerHTML = '';
            doc2LineNumbers.innerHTML = '';
            
            // 处理文档内容
            const doc1Lines = documentData.doc1_content.split('\n');
            const doc2Lines = documentData.doc2_content.split('\n');
            
            // 使用高亮功能显示文档内容
            highlightContent(documentData.doc1_content, documentData.doc2_content);
            
            // 添加同步滚动
            setupSyncScrolling();
        }

        // 高亮相似或差异内容
        function highlightContent(doc1, doc2) {
            const doc1Element = document.getElementById('doc1-content');
            const doc2Element = document.getElementById('doc2-content');
            
            // 清空内容
            doc1Element.innerHTML = '';
            doc2Element.innerHTML = '';
            
            // 分割文本为单词
            const doc1Words = doc1.split(/\s+/);
            const doc2Words = doc2.split(/\s+/);
            
            // 创建文档片段
            const fragment1 = document.createDocumentFragment();
            const fragment2 = document.createDocumentFragment();
            
            // 处理文档1
            doc1Words.forEach(word => {
                const span = document.createElement('span');
                span.textContent = word + ' ';
                
                // 检查是否在文档2中存在
                const existsInDoc2 = doc2Words.includes(word);
                
                if (currentHighlightMode === 'similar' && existsInDoc2) {
                    span.classList.add('highlight', 'similar-highlight');
                } else if (currentHighlightMode === 'diff' && !existsInDoc2) {
                    span.classList.add('highlight', 'diff-highlight');
                }
                
                fragment1.appendChild(span);
            });
            
            // 处理文档2
            doc2Words.forEach(word => {
                const span = document.createElement('span');
                span.textContent = word + ' ';
                
                // 检查是否在文档1中存在
                const existsInDoc1 = doc1Words.includes(word);
                
                if (currentHighlightMode === 'similar' && existsInDoc1) {
                    span.classList.add('highlight', 'similar-highlight');
                } else if (currentHighlightMode === 'diff' && !existsInDoc1) {
                    span.classList.add('highlight', 'diff-highlight');
                }
                
                fragment2.appendChild(span);
            });
            
            // 添加内容到DOM
            doc1Element.appendChild(fragment1);
            doc2Element.appendChild(fragment2);
        }

        // 设置同步滚动
        function setupSyncScrolling() {
            const doc1Content = document.getElementById('doc1-content');
            const doc2Content = document.getElementById('doc2-content');
            
            // 移除之前的监听器
            doc1Content.removeEventListener('scroll', handleDoc1Scroll);
            doc2Content.removeEventListener('scroll', handleDoc2Scroll);
            
            // 添加新的监听器
            doc1Content.addEventListener('scroll', handleDoc1Scroll);
            doc2Content.addEventListener('scroll', handleDoc2Scroll);
        }

        // 处理文档1滚动
        function handleDoc1Scroll() {
            if (!syncScrolling || isScrolling) return;
            
            isScrolling = true;
            const doc1Content = document.getElementById('doc1-content');
            const doc2Content = document.getElementById('doc2-content');
            
            doc2Content.scrollTop = doc1Content.scrollTop;
            
            setTimeout(() => {
                isScrolling = false;
            }, 50);
        }

        // 处理文档2滚动
        function handleDoc2Scroll() {
            if (!syncScrolling || isScrolling) return;
            
            isScrolling = true;
            const doc1Content = document.getElementById('doc1-content');
            const doc2Content = document.getElementById('doc2-content');
            
            doc1Content.scrollTop = doc2Content.scrollTop;
            
            setTimeout(() => {
                isScrolling = false;
            }, 50);
        }

        // 处理排序变化
        function handleSortChange() {
            currentSortField = sortField.value;
            currentSortOrder = sortOrder.value;
            currentPage = 1;
            loadTableData();
        }

        // 处理每页显示数量变化
        function handleItemsPerPageChange() {
            itemsPerPage = parseInt(itemsPerPageSelect.value);
            currentPage = 1;
            loadTableData();
        }

        // 处理语言过滤变化
        function handleLanguageFilterChange() {
            currentLanguageFilter = languageFilter.value;
            currentPage = 1;
            loadTableData();
        }

        // 跳转到首页
        function goToFirstPage() {
            currentPage = 1;
            loadTableData();
        }

        // 跳转到上一页
        function goToPrevPage() {
            if (currentPage > 1) {
                currentPage--;
                loadTableData();
            }
        }

        // 跳转到下一页
        function goToNextPage() {
            // 需要从分页信息获取总页数，这里简单处理
            const totalPages = parseInt(pageInput.max);
            if (currentPage < totalPages) {
                currentPage++;
                loadTableData();
            }
        }

        // 跳转到末页
        function goToLastPage() {
            const totalPages = parseInt(pageInput.max);
            currentPage = totalPages;
            loadTableData();
        }

        // 处理页码输入变化
        function handlePageInputChange() {
            const page = parseInt(pageInput.value);
            const totalPages = parseInt(pageInput.max);
            
            if (page >= 1 && page <= totalPages) {
                currentPage = page;
                loadTableData();
            } else {
                pageInput.value = currentPage;
            }
        }

        // 导出数据
        async function exportData() {
            if (!currentDataId) return;
            
            try {
                const response = await fetch(`/api/export/${currentDataId}`);
                
                if (response.ok) {
                    const blob = await response.blob();
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = `similarity_data_${currentDataId}.json`;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    window.URL.revokeObjectURL(url);
                } else {
                    const result = await response.json();
                    alert('导出失败: ' + result.error);
                }
            } catch (error) {
                alert('导出失败: ' + error.message);
            }
        }

        // 显示加载遮罩
        function showLoading() {
            loadingOverlay.classList.remove('d-none');
        }

        // 隐藏加载遮罩
        function hideLoading() {
            loadingOverlay.classList.add('d-none');
        }

        // 页面加载完成后的初始化
        document.addEventListener('DOMContentLoaded', function() {
            // 检查是否有现有的数据ID（例如从URL参数）
            const urlParams = new URLSearchParams(window.location.search);
            const dataId = urlParams.get('data_id');
            
            if (dataId) {
                currentDataId = dataId;
                // 加载现有数据
                loadTableData();
                loadVisualizationData();
                statsSection.classList.remove('d-none');
                dataSection.classList.remove('d-none');
                visualizationSection.classList.remove('d-none');
            }
        });
    </script>
</body>
</html>
